package edu.drexel.jrexel2d.networking;

import java.io.Serializable;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

import edu.drexel.jrexel2d.networking.socket.GameServerSocket;
import edu.drexel.jrexel2d.networking.socket.GameSocket;
import edu.drexel.jrexel2d.networking.socket.ServerMasterSocket;
import edu.drexel.jrexel2d.networking.sync.HandShake;

public class NetworkServer {

	// private GamePacket receivePacket;
	private int byteSize;
	// private GameSocket gameServerSocket;
	private ServerMasterSocket masterSocket;

	private List<PlayerNetworkInfo> establishedClients;

	public NetworkServer(int port, int byteSize) {
		// this.gameServerSocket = new GameServerSocket(port);
		this.byteSize = byteSize;

		establishedClients = new ArrayList<PlayerNetworkInfo>();
		this.masterSocket = new ServerMasterSocket(port);
		try {
			masterSocket.initialize();
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/*
	 * public void checkForNewClients(){ masterSocket.hasData(); }
	 */

	public boolean acceptConnectionsAndRecievePackets(List<GamePacket> packets) {
		boolean werePacketsAccepted = false;
		if (acceptConnection())
			werePacketsAccepted = true;
		if (recievePacketsFromClients(packets))
			werePacketsAccepted = true;
		return werePacketsAccepted;

	}

	public boolean acceptConnection() {
		GamePacket receivePacket = new GamePacket(byteSize);
		if (!masterSocket.receive(receivePacket))
			return false;
		;

		// check to see if client has already established connection
		// loops through all players that have established a connection
		// creates a new GameServerSocket for new clients and adds them to the
		// list of established clients.
		if (establishedClients.size() == 0) {
			establishConnection(receivePacket);
			return true;
		}

		else {
			for (PlayerNetworkInfo info : establishedClients) {
				if (!(info.getIp().equals(receivePacket.getAddress()) && info
						.getPort() == receivePacket.getPort())) {
					establishConnection(receivePacket);
					return true;
				}
			}
		}
		return false;
	}

	private void establishConnection(GamePacket receivePacket) {
		GameServerSocket serverSocket = masterSocket.acceptConnection(
				receivePacket.getAddress(), receivePacket.getPort());
		establishedClients.add(new PlayerNetworkInfo(
				receivePacket.getAddress(), receivePacket.getPort(),
				serverSocket));

		// send handshake back to client
		HandShake shake = (HandShake) Serializer.deserializeBytes(receivePacket
				.getData());
		shake.setPort(serverSocket.getPort());
		sendSerializable(shake, receivePacket.getPort(),
				receivePacket.getAddress(), masterSocket);
	}

	public List<PlayerNetworkInfo> getEstablishedClients() {
		return establishedClients;

	}

	// TODO: Refactor this if implementing fragmented packets
	public Boolean recievePacketsFromClients(List<GamePacket> packets) {
		for (PlayerNetworkInfo info : establishedClients) {
			GamePacket receivePacket = new GamePacket(byteSize);
			if (info.getServerSocket().receive(receivePacket))
				packets.add(receivePacket);
		}
		return (packets.size() > 0);
	}

	public void sendSerializable(Serializable data, int port,
			InetAddress address, GameSocket serverSocket) {
		byte[] sendDataArray = new byte[byteSize];
		sendDataArray = Serializer.serializeSerializable(data);
		GamePacket sendPacket = new GamePacket(sendDataArray,
				sendDataArray.length, address, port);
		serverSocket.send(sendPacket);
	}

}
